#include "ExmcInitHelper.h"

#include <string.h>
#include <stdlib.h>

#include <rtthread.h>

#define DMC_MAX_TIMEOUT (0x100000UL)

uint32_t m_u32StartAddr = 0UL;
uint32_t m_u32ByteSize = 0UL;

//struct rt_memheap  *sdram1_heap;
__attribute__((at(0x80000000))) char sdram_mp_pool[0x20000];
struct rt_mempool sdram1_mp;

//uint8_t m_au8ReadData[DATA_BUF_LEN];
//uint8_t m_au8WriteData[DATA_BUF_LEN];

//uint16_t m_au16ReadData[DATA_BUF_LEN];
//uint16_t m_au16WriteData[DATA_BUF_LEN];

//uint32_t m_au32ReadData[DATA_BUF_LEN];
//uint32_t m_au32WriteData[DATA_BUF_LEN];

//uint32_t m_u32TestCnt = 0UL;
//uint32_t m_u32ByteTestErrorCnt = 0UL;
//uint32_t m_u32HalfwordTestErrorCnt = 0UL;
//uint32_t m_u32WordTestErrorCnt = 0UL;

//int32_t MEMORY_Write8(uint32_t u32Addr, const uint8_t au8Data[], uint32_t u32Len)
//{
//    uint32_t i;
//    int32_t i32Ret = LL_ERR_INVD_PARAM;

//    if ((au8Data != NULL) && (0UL != u32Len)) {
//        for (i = 0UL; i < u32Len; i++) {
//            *(uint8_t *)(u32Addr + i) = au8Data[i];
//        }
//        i32Ret = LL_OK;
//    }

//    return i32Ret;
//}

///**
// * @brief  Read memory for byte.
// * @param  [in] u32Addr                 Memory address to read
// * @param  [out] au8Data                Pointer to buffer
// * @param  [in] u32Len                  Length of the buffer to read from memory
// * @retval int32_t:
// *           - LL_OK:                   Read successfully.
// *           - LL_ERR_INVD_PARAM:       If one of following cases matches:
// *                                      -The pointer au8Data value is NULL.
// *                                      -The variable u32Len value is 0.
// */
//int32_t MEMORY_Read8(uint32_t u32Addr, uint8_t au8Data[], uint32_t u32Len)
//{
//    uint32_t i;
//    int32_t i32Ret = LL_ERR_INVD_PARAM;

//    if ((NULL != au8Data) && (0UL != u32Len)) {
//        for (i = 0UL; i < u32Len; i++) {
//            au8Data[i] = *(uint8_t *)(u32Addr + i);
//        }
//        i32Ret = LL_OK;
//    }

//    return i32Ret;
//}

///**
// * @brief  Write memory for half-word.
// * @param  [in] u32Addr                 Memory address to write
// * @param  [in] au16Data                Pointer to buffer
// * @param  [in] u32Len                  Length of the buffer to write to memory
// * @retval int32_t:
// *           - LL_OK:                   Write successfully.
// *           - LL_ERR_INVD_PARAM:       If one of following cases matches:
// *                                      -The pointer au16Data value is NULL.
// *                                      -The variable u32Len value is 0.
// */
//int32_t MEMORY_Write16(uint32_t u32Addr, const uint16_t au16Data[], uint32_t u32Len)
//{
//    uint32_t i;
//    int32_t i32Ret = LL_ERR_INVD_PARAM;

//    if ((au16Data != NULL) && (0UL != u32Len)) {
//        for (i = 0UL; i < u32Len; i++) {
//            *((uint16_t *)u32Addr) = au16Data[i];
//            u32Addr += 2UL;
//        }
//        i32Ret = LL_OK;
//    }

//    return i32Ret;
//}

///**
// * @brief  Read memory for half-word.
// * @param  [in] u32Addr                 Memory address to read
// * @param  [out] au16Data               Pointer to buffer
// * @param  [in] u32Len                  Length of the buffer to read from memory
// * @retval int32_t:
// *           - LL_OK:                   Read successfully.
// *           - LL_ERR_INVD_PARAM:       If one of following cases matches:
// *                                      -The pointer au16Data value is NULL.
// *                                      -The variable u32Len value is 0.
// */
//int32_t MEMORY_Read16(uint32_t u32Addr, uint16_t au16Data[], uint32_t u32Len)
//{
//    uint32_t i;
//    int32_t i32Ret = LL_ERR_INVD_PARAM;

//    if ((NULL != au16Data) && (0UL != u32Len)) {
//        for (i = 0UL; i < u32Len; i++) {
//            au16Data[i] = *((uint16_t *)u32Addr);
//            u32Addr += 2UL;
//        }
//        i32Ret = LL_OK;
//    }

//    return i32Ret;
//}

///**
// * @brief  Write memory for word.
// * @param  [in] u32Addr                 Memory address to write
// * @param  [in] au32Data                Pointer to source buffer
// * @param  [in] u32Len                  Length of the buffer to write to memory
// * @retval int32_t:
// *           - LL_OK:                   Read successfully.
// *           - LL_ERR_INVD_PARAM:       If one of following cases matches:
// *                                      -The pointer au32Data value is NULL.
// *                                      -The variable u32Len value is 0.
// */
//int32_t MEMORY_Write32(uint32_t u32Addr, const uint32_t au32Data[], uint32_t u32Len)
//{
//    uint32_t i;
//    int32_t i32Ret = LL_ERR_INVD_PARAM;

//    if ((NULL != au32Data) && (0UL != u32Len)) {
//        for (i = 0UL; i < u32Len; i++) {
//            *((uint32_t *)u32Addr) = au32Data[i];
//            u32Addr += 4UL;
//        }
//        i32Ret = LL_OK;
//    }

//    return i32Ret;
//}

///**
// * @brief  Read memory for word.
// * @param  [in] u32Addr                 Memory address to read
// * @param  [out] au32Data               Pointer to destination buffer to write
// * @param  [in] u32Len                  Length of the buffer to read from memory
// * @retval int32_t:
// *           - LL_OK:                   Read successfully.
// *           - LL_ERR_INVD_PARAM:       If one of following cases matches:
// *                                      -The pointer au32Data value is NULL.
// *                                      -The variable u32Len value is 0.
// */
//int32_t MEMORY_Read32(uint32_t u32Addr, uint32_t au32Data[], uint32_t u32Len)
//{
//    uint32_t i;
//    int32_t i32Ret = LL_ERR_INVD_PARAM;

//    if ((NULL != au32Data) && (0UL != u32Len)) {
//        for (i = 0UL; i < u32Len; i++) {
//            au32Data[i] = *((uint32_t *)u32Addr);
//            u32Addr += 4UL;
//        }
//        i32Ret = LL_OK;
//    }

//    return i32Ret;
//}

//uint32_t MEMORY_Access8(uint32_t u3AccessAddr, uint32_t NumBytes)
//{
//    uint32_t i;
//    uint32_t j;
//    uint32_t u32TestErrCnt = 0UL;
//    uint32_t u32MemoryAddr = u3AccessAddr;
//	
//	rt_kprintf("test reg 8\n");
//	rt_thread_mdelay(1);

//    for (i = 0UL; i < NumBytes; i += DATA_BUF_LEN) {
//        (void)MEMORY_Write8(u32MemoryAddr, m_au8WriteData, DATA_BUF_LEN);
//        (void)MEMORY_Read8(u32MemoryAddr, m_au8ReadData, DATA_BUF_LEN);

//        /* Verify data. */
//        for (j = 0UL; j < DATA_BUF_LEN; j++) {
//            if (m_au8WriteData[j] != m_au8ReadData[j]) {
//                u32TestErrCnt++;
//                rt_kprintf("Byte read/write error: address = 0x%.8x; write data = 0x%x; read data = 0x%x\n",
//                           (unsigned int)(u32MemoryAddr + j * sizeof(m_au8ReadData[0])), (unsigned int)m_au8WriteData[j], (unsigned int)m_au8ReadData[j]);
//							rt_thread_mdelay(1);
//            }
//        }

//        u32MemoryAddr += (DATA_BUF_LEN * sizeof(m_au8ReadData[0]));
//        (void)memset(m_au8ReadData, 0, (DATA_BUF_LEN * sizeof(m_au8ReadData[0])));
//        rt_kprintf("access a 8 reg!\n");
//				rt_thread_mdelay(1);
//    }

//    return u32TestErrCnt;
//}

//uint32_t MEMORY_Access16(uint32_t u3AccessAddr, uint32_t NumHalfwords)
//{
//    uint32_t i;
//    uint32_t j;
//    uint32_t u32TestErrCnt = 0UL;
//    uint32_t u32MemoryAddr = u3AccessAddr;

//    for (i = 0UL; i < NumHalfwords; i += DATA_BUF_LEN) {
//        (void)MEMORY_Write16(u32MemoryAddr, m_au16WriteData, DATA_BUF_LEN);
//        (void)MEMORY_Read16(u32MemoryAddr, m_au16ReadData, DATA_BUF_LEN);

//        /* Verify data. */
//        for (j = 0UL; j < DATA_BUF_LEN; j++) {
//            if (m_au16WriteData[j] != m_au16ReadData[j]) {
//                u32TestErrCnt++;
//                rt_kprintf("Halfword read/write error: address = 0x%.8x; write data = 0x%x; read data = 0x%x\n",
//                           (unsigned int)(u32MemoryAddr + j * sizeof(m_au16ReadData[0])), (unsigned int)m_au16WriteData[j], (unsigned int)m_au16ReadData[j]);
//							rt_thread_mdelay(1);
//            }
//        }

//        u32MemoryAddr += (DATA_BUF_LEN * sizeof(m_au16ReadData[0]));
//        (void)memset(m_au16ReadData, 0, (DATA_BUF_LEN * sizeof(m_au16ReadData[0])));
//        rt_kprintf("access a 16 reg!\n");
//				rt_thread_mdelay(1);
//    }

//    return u32TestErrCnt;
//}

//uint32_t MEMORY_Access32(uint32_t u3AccessAddr, uint32_t NumWords)
//{
//    uint32_t i;
//    uint32_t j;
//    uint32_t u32TestErrCnt = 0UL;
//    uint32_t u32MemoryAddr = u3AccessAddr;

//    for (i = 0UL; i < NumWords; i += DATA_BUF_LEN) {
//        (void)MEMORY_Write32(u32MemoryAddr, m_au32WriteData, DATA_BUF_LEN);
//        (void)MEMORY_Read32(u32MemoryAddr, m_au32ReadData, DATA_BUF_LEN);

//        /* Verify data. */
//        for (j = 0UL; j < DATA_BUF_LEN; j++) {
//            if (m_au32WriteData[j] != m_au32ReadData[j]) {
//                u32TestErrCnt++;
//                rt_kprintf("Word read/write error: address = 0x%.8x; write data = 0x%.8x; read data = 0x%.8x\n",
//                           (unsigned int)(u32MemoryAddr + j * sizeof(m_au32ReadData[0])), (unsigned int)m_au32WriteData[j], (unsigned int)m_au32ReadData[j]);
//							rt_thread_mdelay(1);
//            }
//        }

//        u32MemoryAddr += (DATA_BUF_LEN * sizeof(m_au32ReadData[0]));
//        (void)memset(m_au32ReadData, 0, (DATA_BUF_LEN * sizeof(m_au32ReadData[0])));
//        rt_kprintf("access a 32 reg!\n");
//				rt_thread_mdelay(1);
//    }

//    return u32TestErrCnt;
//}

//void InitTestData(void)
//{
//    uint32_t i;

//    /* Clear count value */
//    m_u32ByteTestErrorCnt = 0UL;
//    m_u32HalfwordTestErrorCnt = 0UL;
//    m_u32WordTestErrorCnt = 0UL;

//    /* Initialize test data. */
//    for (i = 0UL; i < DATA_BUF_LEN; i++) {
//        m_au8ReadData[i] = 0U;
//        m_au16ReadData[i] = 0U;
//        m_au32ReadData[i] = 0UL;
//        m_au8WriteData[i] = (uint8_t)0x8;
//        m_au16WriteData[i] = (uint16_t)0x16;
//        m_au32WriteData[i] = (uint32_t)0x32;
//    }
//}

void BSP_Sdram_GetMemInfo(uint32_t *pu32MemoryStartAddr, uint32_t *pu32MemoryByteSize)
{
    if (NULL != pu32MemoryStartAddr) {
        *pu32MemoryStartAddr = BSP_Sdram1_START_ADDR;
    }

    if (NULL != pu32MemoryByteSize) {
        *pu32MemoryByteSize = BSP_Sdram1_SIZE;
    }
}

void BSP_SDRAM_InitSequence(uint32_t u32Chip, uint32_t u32Bank, uint32_t u32MdRegValue) {
  /* SDRAM initialization sequence:
       CMD NOP->PrechargeAll->AutoRefresh->AutoRefresh->MdRegConfig->NOP */
  (void) EXMC_DMC_SetCommand(u32Chip, u32Bank, EXMC_DMC_CMD_NOP, 0UL);
  (void) EXMC_DMC_SetCommand(u32Chip, u32Bank, EXMC_DMC_CMD_PRECHARGE_ALL, 0UL);
  (void) EXMC_DMC_SetCommand(u32Chip, u32Bank, EXMC_DMC_CMD_AUTO_REFRESH, 0UL);
  (void) EXMC_DMC_SetCommand(u32Chip, u32Bank, EXMC_DMC_CMD_AUTO_REFRESH, 0UL);
  (void) EXMC_DMC_SetCommand(u32Chip, u32Bank, EXMC_DMC_CMD_MDREG_CONFIG, u32MdRegValue);
  (void) EXMC_DMC_SetCommand(u32Chip, u32Bank, EXMC_DMC_CMD_NOP, 0UL);
}

void App_SdramCfg(void) {
  __IO uint32_t u32To = 0UL;
  uint32_t u32MdRegValue;
//  int32_t i32Ret = LL_OK;
  stc_exmc_dmc_init_t stcDmcInit;
  stc_exmc_dmc_chip_config_t stcCsConfig;

  /* Initialization DMC port.*/
  // 这里因为在port已经初始化了。这里就不初始化了
  //    BSP_DMC_PortInit();

  /* Enable DMC clock */
  FCG_Fcg3PeriphClockCmd(FCG3_PERIPH_DMC, ENABLE);

  /* Enable DMC. */
  EXMC_DMC_Cmd(ENABLE);

  /* Configure DMC width && refresh period & chip & timing. */
  (void) EXMC_DMC_StructInit(&stcDmcInit);
	// 刷新速度120M，也就是EXCLK的速度。
	// 自刷新周期 64ms除以8.33ns -1 这里为(64000 / 8.33) - 1 = 7683-1 = 7682 
	// 120M速度每个时钟为8.33ns
  stcDmcInit.u32RefreshPeriod = 7682UL;
	// 行列地址数。和存储容量有关。可以看看手册的存储结构示意图
  stcDmcInit.u32ColumnBitsNumber = EXMC_DMC_COLUMN_BITS_NUM9;
  stcDmcInit.u32RowBitsNumber = EXMC_DMC_ROW_BITS_NUM13;
	// 连续突发传输个数。暂时不知道啥用。默认
  stcDmcInit.u32MemBurst = EXMC_DMC_BURST_1BEAT;
	// 设置自动刷新。这里不能单独设置某个自动刷新，而是从第0个开始，到第N个
  stcDmcInit.u32AutoRefreshChips = EXMC_DMC_AUTO_REFRESH_1CHIP;

  /* EXCLK bus frequency@120MHz: 3.3V 每个时钟8.33ns*/
  /* 从出发时钟周期，也就是变位后第一个周期到读取到翻转的周期的间隔，一般就是数数上升沿数量-1 */
  stcDmcInit.stcTimingConfig.u8CASL = 3U; /* CAS latency 4clk |*/
  stcDmcInit.stcTimingConfig.u8DQSS = 2U; /* 写延迟，一般取决于DQS DQ DQM三个时间的最长跨度，但是一般取决于最长的DQS 大多为1个clk上下 |*/
  stcDmcInit.stcTimingConfig.u8MRD = 2U;   /* tMRD,mode register command time,有的手册不写MRD而是tRSC,都是一个参数的不同缩写: 2CLK |*/
  stcDmcInit.stcTimingConfig.u8RAS = 6U;   /* tRAS: min=42ns |*/
  stcDmcInit.stcTimingConfig.u8RC = 8U;    /* tRC:  min=60ns |*/
  stcDmcInit.stcTimingConfig.u8RCD_B = 2U; /* tRCD: min=15ns |*/ 
  stcDmcInit.stcTimingConfig.u8RCD_P = 0U; // 附加RCD时间。一般SDRAM的附加时间都是0 |
  stcDmcInit.stcTimingConfig.u8RFC_B = 8U; /* tRFC: min=63ns */
  stcDmcInit.stcTimingConfig.u8RFC_P = 0U;
  stcDmcInit.stcTimingConfig.u8RP_B = 2U; /* tRP:  min=15ns |*/
  stcDmcInit.stcTimingConfig.u8RP_P = 0U; /* 附加tRP周期:  0 |*/
  stcDmcInit.stcTimingConfig.u8RRD = 2U; /* tRRD: min=2CLK |*/
  stcDmcInit.stcTimingConfig.u8WR = 2U;  /* tWR写恢复时间:  2CLK |*/
  stcDmcInit.stcTimingConfig.u8WTR = 1U; /* tWR或tCCD write to write command period 写保护时间,也就是两次写命令最小间隔:  1CLK |*/
  stcDmcInit.stcTimingConfig.u8XP = 1U;  /* power down模式退出时间 0-6ns,本芯片给出的标准时间为5ns |*/
  stcDmcInit.stcTimingConfig.u8XSR = 9U; /* tXSR（exit self-refresh command time）: min=72ns |*/
  stcDmcInit.stcTimingConfig.u8ESR = 17U; /* tESR（self-refresh command time）: 进入tXSR前大概8个时钟周期 |*/
  (void) EXMC_DMC_Init(&stcDmcInit);

  /* Configure DMC address space. */
	// 地址掩码。指向SDRAM的容量空间
  stcCsConfig.u32AddrMask = EXMC_DMC_ADDR_MASK_32MB;
	// 指定CS0空间。这个要查手册 CS0 设定和访问地址对应关系表
  stcCsConfig.u32AddrMatch = 0x80UL;
	// 地址顺序 行地址 BANK地址 列地址。也就是标准地址
  stcCsConfig.u32AddrDecodeMode = EXMC_DMC_CS_DECODE_ROWBANKCOL;
  (void) EXMC_DMC_ChipConfig(EXMC_DMC_CHIP0, &stcCsConfig);

  /* SDRAM initialization sequence. */
  u32MdRegValue = ((0UL) | (1UL << 3));
  if (2U == stcDmcInit.stcTimingConfig.u8CASL) {
    u32MdRegValue |= (2UL << 4);
  } else {
    u32MdRegValue |= (3UL << 4);
  }

  if (EXMC_DMC_BURST_1BEAT == stcDmcInit.u32MemBurst) {
    u32MdRegValue |= (0UL);
  } else if (EXMC_DMC_BURST_2BEAT == stcDmcInit.u32MemBurst) {
    u32MdRegValue |= (1UL);
  } else if (EXMC_DMC_BURST_4BEAT == stcDmcInit.u32MemBurst) {
    u32MdRegValue |= (2UL);
  } else {
    u32MdRegValue |= (3UL);
  }

  BSP_SDRAM_InitSequence(EXMC_DMC_CHIP0, EXMC_DMC_BANK0, u32MdRegValue);

  /* Switch state from configure to ready */
  EXMC_DMC_SetState(EXMC_DMC_CTRL_STATE_GO);
  EXMC_DMC_SetState(EXMC_DMC_CTRL_STATE_WAKEUP);
  EXMC_DMC_SetState(EXMC_DMC_CTRL_STATE_GO);

  /* Check status */
  while (EXMC_DMC_CURR_STATUS_RDY != EXMC_DMC_GetStatus()) {
    if (u32To > DMC_MAX_TIMEOUT) {
      rt_kprintf("sdram dmc init timeout\n");
      break;
    }
    u32To++;
  }
  
  //rt_memheap_init(sdram1_heap, "sdram1_heap", 0x80000000, 32 * 1024 * 1024);
  //rt_mp_init(&sdram1_mp, "sdram1_mp", sdram_mp_pool, 0x20000, 256);
//  return i32Ret;
}
